/*  
  Copyright 2007-2009 The NGenerics Team
 (http://code.google.com/p/ngenerics/wiki/Team)

 This program is licensed under the GNU Lesser General Public License (LGPL).  You should 
 have received a copy of the license along with the source code.  If not, an online copy
 of the license can be found at http://www.gnu.org/copyleft/lesser.html.
*/

using System;
using System.Collections.Generic;

namespace NGenerics.DataStructures.Immutable
{
    /// <summary>
    /// An immutable class representing a pair of values with value semantics.
    /// </summary>
    /// <remarks>
    /// <para>The guarantees of this class are only as strong as those of 
    /// <typeparamref name="TA"/> and <typeparamref name="TB"/>.</para>
    /// <para>Since this class is immutable, the recipient of this class can 
    /// be assured, that under no circumstances the value, hashcode or 
    /// identity change. This can be useful in a number of scenarios, 
    /// beginning with multi-treading and the use as key in a 
    /// <see cref="System.Collections.Generic.Dictionary{K,V}"/>.</para>
    /// <para>
    /// The value semantics imply that two different instances of this class 
    /// which were constructed with the same arguments can only be 
    /// distinguished by using <see cref="Object.ReferenceEquals(object,object)"/>.
    /// </para>
    /// </remarks>
    /// <typeparam name="TA">The first type the pair contains.</typeparam>
    /// <typeparam name="TB">The other type the pair contains.</typeparam>
#if (!SILVERLIGHT)
    [Serializable]
#endif
    public class Pair<TA, TB>
    {
        #region Globals

        /// <summary>Private backing store for the <see cref="A"/> property.</summary>
        private readonly TA a;

        /// <summary>Private backing store for the <see cref="B"/> property.</summary>
        private readonly TB b;

        #endregion

        #region Construction

        /// <summary>
        /// Initializes a new instance of the Pair class, using the specified values.
        /// </summary>
        /// <param name="a">The first value.</param>
        /// <param name="b">The other value.</param>
        public Pair(TA a, TB b)
        {
            this.a = a;
            this.b = b;
        }

        #endregion

        #region Public Members

        /// <summary>
        /// Gets the A-half of this <see cref="Pair{TA,TB}"/>.
        /// </summary>
        /// <value>The A-half.</value>
        public TA A
        {
            get
            {
                return a;
            }
        }

        /// <summary>
        /// Gets the B-half of this <see cref="Pair{TA,TB}"/>.
        /// </summary>
        /// <value>The B-half.</value>
        public TB B
        {
            get
            {
                return b;
            }
        }

        /// <summary>
        /// Creates a new <see cref="Pair{TA,TB}"/> with A and B switched.
        /// </summary>
        /// <returns>
        /// This method returns null on null input or a new <see cref="Pair{TB,TA}"/> with its contents switched.
        /// </returns>
        public Pair<TB, TA> Reverse()
        {
            return new Pair<TB, TA>(b, a);
        }

        /// <summary>
        /// Return a new <see cref="System.Collections.Generic.KeyValuePair{TA,TB}"/> with the contents of the specified Pair.
        /// </summary>
        /// <returns>
        /// This method returns a new <see cref="KeyValuePair{TKey,TValue}"/> containing <see cref="Pair{TA,TB}.A"/> as key and <see cref="Pair{TA,TB}.B"/> as value.
        /// </returns>
        public KeyValuePair<TA, TB> ToKeyValuePair()
        {
            return new KeyValuePair<TA, TB>(a, b);
        }

        #endregion

        #region Operator Overloads

        /// <summary>
        /// Compares the two Pairs based on their constituents.
        /// </summary>
        /// <param name="left">The first element to compare.</param>
        /// <param name="right">The second element to compare.</param>
        /// <returns>true, if both pairs have the same contents; false,otherwise.</returns>
        public static bool operator ==(Pair<TA, TB> left, Pair<TA, TB> right)
        {
            // shortcut reference equality
            if (ReferenceEquals(left, right))
            {
                return true;
            }

            return ReferenceEquals(left, null) || left.Equals(right);
        }

        /// <summary>
        /// Compares the two Pairs based on their constituents.
        /// </summary>
        /// <param name="left">The first element to compare.</param>
        /// <param name="right">The second element to compare.</param>
        /// <returns>false, if both pairs have the same contents; true,otherwise.</returns>
        public static bool operator !=(Pair<TA, TB> left, Pair<TA, TB> right)
        {
            return !(left == right);
        }

        #endregion

        #region Object Overrides

        /// <summary>
        /// Creates a user-readable string representation of this Pair.
        /// </summary>
        /// <returns>A user-readable string.</returns>
        public override string ToString()
        {
            return String.Format("({0}, {1})", a, b);
        }

        /// <summary>
        /// Compares this Pair to another Pair, based on their parts.
        /// </summary>
        /// <param name="obj">The object to compare to.</param>
        /// <returns>If the <paramref name="obj"/> is a pair of the same objects as this pair, returns true. Returns false otherwise.</returns>
        public override bool Equals(object obj)
        {
            // shortcut reference equality
            if (ReferenceEquals(this, obj))
            {
                return true;
            }

            var other = obj as Pair<TA, TB>;

            if (other == null)
            {
                return false;
            }

            return Equals(a, other.a) && Equals(b, other.b);
        }

        /// <summary>
        /// Calculates a hash code from this pair's constituents.
        /// </summary>
        /// <returns>A hashcode suitable for use in Hastables and similar structures.</returns>
        public override int GetHashCode()
        {
            return (ReferenceEquals(a, null) ? 0 : a.GetHashCode())
                ^ (ReferenceEquals(b, null) ? 0 : b.GetHashCode());
        }

        #endregion
    }
}